/**
 * 
 */
package mdp.id;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import mdp.id.RDDL2DD.DEBUG;

import dd.discrete.ADD;
import dd.discrete.ADDDNode;
import dd.discrete.ADDINode;
import dd.discrete.ADDNode;
import dd.discrete.DD;

import rddl.RDDL;
import rddl.RDDL.DOMAIN;
import rddl.RDDL.INSTANCE;
import rddl.RDDL.LCONST;
import rddl.RDDL.NONFLUENTS;
import rddl.State;
import rddl.parser.parser;
import rddl.RDDL.PVAR_NAME;
import util.Pair;
import util.Timer;

/**
 * @author NR
 *
 * TODO
 */
public class ADDHandler extends RDDL2DD<ADD> {

	private Map<Pair<Integer, Double>, Integer>	makeNegInfCache = new HashMap<Pair<Integer,Double>, Integer>();
	private int	policyHit = 0;
	private Map<Pair<Integer, Map<String, Boolean>>, Integer> policyCache = new HashMap<Pair<Integer, Map<String, Boolean>>, Integer>();
	private Timer constrainTimer = new Timer();
	private Map<Integer, Integer> detPolicyCache = new HashMap<Integer, Integer>();
	private Map<Pair<Integer, Integer>, Integer> expectationCache = new HashMap<Pair<Integer,Integer>, Integer>();
	public static boolean CONSTRAIN_NAIVELY = false;
	
	/**
	 * @param pSolutionInfo
	 */
	public ADDHandler(DEBUG dbg, ORDER ord) {
		_debug = dbg;
		_order = ord;
	}

	/* (non-Javadoc)
	 * @see mdp.id.RDDL2DD#createInstance()
	 */
	@Override
	protected ADD createInstance() {
		return new ADD(_alOrder);
	}

	/* (non-Javadoc)
	 * @see mdp.id.RDDL2DD#getMax(int)
	 */
	@Override
	public double getMax(int dd) {
		return _context.getMax(dd);
	}

	@Override
	public double getConstrainTime(){
//		System.out.println(_context.constrainHit);
		return constrainTimer.GetElapsedTimeInMinutes();
	}
	/* (non-Javadoc)
	 * @see mdp.id.RDDL2DD#getMin(int)
	 */
	@Override
	public double getMin(int dd) {
		return _context.getMin(dd);
	}

	/* (non-Javadoc)
	 * @see mdp.id.RDDL2DD#getNegInf()
	 */
	@Override
	public double getNegInf() {
		return ADD.NEGINF;
	}

	@Override
	public void showGraph(int... dds) {
		for( int dd : dds ){
			_context.getGraph(dd, _tmID2VarName).launchViewer();	
		}
	}

	@Override
	public int ConstrainThis(int dd, Map<Integer, Boolean> act,
			boolean flush,
			final int... save) {
		
		constrainTimer.ResumeTimer();
		//changed on 2/17/2012
		//to use much more efficient constrainRestrict instead of naive multiplication
		//but this needs to be applied after every op
		
		if( _debug.compareTo(DEBUG.SOLUTION_INFO) >= 0 ) {
			System.out.println("Constraining " + getSize(dd));
			System.out.println(showMemory());
		}
		int ret = dd;
		
//		for( Pair<Integer, Integer> con : _hsPairConstraints ) {
//			int constraint = con._o1;
//			
////			showGraph(constraint);
////			showGraph(ret);
//			
////			ret = _context.applyInt(ret, constraint, DD.ARITH_SUM);
//			ret = _context.constrainRestrict(ret, constraint);	
//			
////			showGraph(ret);
//			
//			
//			
//		}
		if( CONSTRAIN_NAIVELY ){
			ret = _context.applyInt(ret, combinedConstraint, DD.ARITH_PROD);
		}else{
			ret = _context.constrainRestrict(ret, combinedConstraint);	
		}
		
		if( flush ) {
			saveThis(ret);
			flushCaches(false, save);
			forgetThis(ret);
		}
		
		constrainTimer .PauseTimer();
		return ret;
	}
	
//	private Set<Integer> getConstraints(Set gids, Map<Integer, Boolean> act){
//		
//		if( act == null ){
//			act = new HashMap<Integer, Boolean>();
//		}
//		
//			
//		Set<Integer> quantifiedConstraints = new HashSet<Integer>();
//		
//		for( Pair<Integer, Integer> con : _hsPairConstraints ){
//			int constraint = con._o1;
//			constraint = isExogenous(constraint) ? constraint : simplify(constraint, act);
//			//now quantify
//			Set<Integer> gidscon = _context.getGIDs(constraint);
//			gidscon.removeAll(gids);
//			for( Integer i : gidscon ){
//				constraint = _context.opOut(constraint, i, DD.LOG_OR);
//			}
//			constraint = makeNegInfDD(constraint, 0.0d);
//			quantifiedConstraints.add(constraint);
//			
//		}
//			
//		return quantifiedConstraints;
//		
//	}
	
//	private int argMaxHelper(int dd) {
//		ADDNode node = _context.getNode(dd);
//		if( node instanceof ADDDNode ){
//			return DD_ZERO;
//		}
//		
//		ADDINode inode = (ADDINode)node;
//		
//		int varid = inode._nTestVarID;
//		String var = getActualVarName(varid);
//		
//		if( _tmStateVars.get(var) != null ) {
//			int truebr = _context.getVarNode(varid, 0.0d, 1.0d);
//			int truedd = argMaxHelper(inode._nHigh);
//			if( truedd == DD_NEG_INF ) {
//				truebr = _context.getVarNode(varid, 0.0d, getNegInf());
//			}else {
//				truebr = _context.applyInt(truebr, truedd, DD.ARITH_PROD);
//			}
//			
//			int falsebr = _context.getVarNode(varid, 1.0d, 0.0d);
//			int falsedd =  argMaxHelper(inode._nLow);
//			if( falsedd == DD_NEG_INF ) {
//				falsebr = _context.getVarNode(varid, getNegInf(), 0.0d);
//			}else {
//				falsebr = _context.applyInt(falsebr, falsedd, DD.ARITH_PROD);
//			}
//			
//			int ret = _context.applyInt(falsebr, truebr , DD.ARITH_SUM);
//			return ret;
//		}else if( _tmActionVars.get(var) != null ) {
//			
//			int ret = DD_ZERO;
//			
//			while( true ){
//				
//				
//					if( node instanceof ADDINode ) {
//						ADDINode nod = (ADDINode)node;
//						ADDNode truthnode = _context.getNode(nod._nHigh);
//						ADDNode falsenode = _context.getNode(nod._nLow);						
//						double max = nod._dMaxUpper;
//						double maxt, maxf;
//					
//						if( truthnode instanceof ADDDNode ) {
//							maxt = ((ADDDNode)truthnode)._dUpper;
//						}else {
//							maxt = ((ADDINode)truthnode)._dMaxLower;
//						}
//					
//						if( falsenode instanceof ADDDNode ) {
//							maxf = ((ADDDNode)falsenode)._dUpper;
//						}else {
//							maxf = ((ADDINode)falsenode)._dMaxLower;
//						}
//					
//						int thiscon;
//						if( max == maxf ) {
//							node = falsenode;
//							thiscon = _context.getVarNode(nod._nTestVarID, 0.0d, getNegInf());
//						}else {
//							node = truthnode;
//							thiscon = _context.getVarNode(nod._nTestVarID, getNegInf(), 0.0d);
//						}
//						ret = _context.applyInt(ret, thiscon, DD.ARITH_SUM);
//					}else {
//						break;
//					}
//				}
//			return ret;
//		}else{
//		System.err.println("next state!!!");
//		System.exit(1);
//		}
//			
//		return -1;
//		
//	}

	/* (non-Javadoc)
	 * @see mdp.id.RDDL2DD#argMaxToNegInf(int, int)
	 */
	@Override
	public int argMaxToNegInf(int pV, int pQ) {

		int actone = makeBDDPolicy(pV, pQ); 
		
		int ret = makeNegInfDD(actone, 0.0d);
		ret = _context.applyInt(ret, DD_ONE, DD.ARITH_MINUS);
		return ret;
	}
	
	private int makePolicyBDD(int v, int q){

		int diff = _context.applyInt(v, q, DD.ARITH_MINUS);
		diff = ConstrainThis(diff, null, false);

		int bddpolicy = thresholdAt(diff, 0.0d, true);
		bddpolicy = makeBDDDetPolicy(bddpolicy);
		
		return bddpolicy;
	}
	
	@Override
	public int makePolicy(int v, int q){
		
		int actone = makePolicyBDD(v,q);
		
		int ret = makePolicyHelper(actone, new HashMap<String,Boolean>());
		
		policyHit = 0;
		policyCache.clear();
		
	  if( _debug.compareTo(DEBUG.SOLUTION_INFO) >= 0 ){
  		checkConstraints( v, q );
      }
		  
		return ret;
	}
	
//	@Override
//	public int makeADDPolicy(int bdd) {
//		policyCache.clear();
//		int ret = makePolicyHelper(bdd, new HashMap<String, Boolean>());
////		ret = ConstrainThis(ret, null, false);
//		
//	  if( _debug.compareTo(DEBUG.SOLUTION_INFO) >= 0 ){
//      		checkConstraints( bdd, ret );
//      }
//		  
//		return ret;
//	}
//	
	@Override
	public int makePolicyHelper(int dd, Map<String, Boolean> assign) {
		
		if( policyCache.get(new Pair<Integer, Map<String, Boolean>>(dd, assign)) != null ) {
			++policyHit ;
			return policyCache.get(new Pair<Integer, Map<String, Boolean>>(dd, assign));
		}
		
		ADDNode node = _context.getNode(dd);
		if( node instanceof ADDDNode ){
			if( ((ADDDNode)node)._dLower == 1 ) {
				Map<Integer, Boolean> ints = convertToInts(assign);
				long actid = getJointActionID(ints);
				return _context.getConstantNode(actid);
			}else {
				return DD_ZERO;
			}
			
		}
		
		ADDINode inode = (ADDINode)node;
		
		int varid = inode._nTestVarID;
		String var = getActualVarName(varid);
		int ret;
		
		Map<String, Boolean> thisAssign = new HashMap<String, Boolean>(assign);
		if( _alactionvars.contains(var) ) {
			thisAssign.put(var, true);
			int truedd = makePolicyHelper(inode._nHigh, thisAssign);
			
			thisAssign.put(var, false);
			int falsedd =  makePolicyHelper(inode._nLow, thisAssign);
			
			//places where truedd does have an action
			//truedd is zero only for constraint violation
			int zeroOut = thresholdAt(truedd, 0.0d, false);
			zeroOut = _context.applyInt(DD_ONE, zeroOut, DD.ARITH_MINUS);
			
			ret = _context.applyInt(
					truedd, 
					_context.applyInt(falsedd, zeroOut, DD.ARITH_PROD), 
				DD.ARITH_SUM);
		}else {
			int truedd = makePolicyHelper(inode._nHigh, thisAssign);
			int falsedd =  makePolicyHelper(inode._nLow, thisAssign);
			
			ret = combine(varid, truedd, falsedd);
		}
		
		ret = ConstrainThis(ret, null, false);
		ret = fix(ret);
		
		policyCache.put(new Pair<Integer,Map<String,Boolean>>(dd, assign),ret);
		
		return ret;
	}
	
	@Override
	public int getMaximalPath(int dd) {
		
		TreeMap<String, Boolean> actAssigns = new TreeMap<String, Boolean>();
		ADDNode nod = _context.getNode(dd);
		
		while( true ){
			if( nod instanceof ADDDNode ){
				break;
			}else{
				ADDINode n = (ADDINode)nod;

				int varid = n._nTestVarID;
				String var = getActualVarName(varid);
				
//				Syste
				Pair<PVAR_NAME, ArrayList<LCONST>> act = _tmActionVars.get(var);
				Boolean defaultval = (Boolean) _state.getDefaultValue(act._o1);
				
				ADDNode truthnode = _context.getNode(n._nHigh);
				ADDNode falsenode = _context.getNode(n._nLow);
				
				double max = n._dMaxUpper;
				double maxt, maxf;
				
				if( truthnode instanceof ADDDNode ) {
					maxt = ((ADDDNode)truthnode)._dUpper;
				}else {
					maxt = ((ADDINode)truthnode)._dMaxLower;
				}
				
				if( falsenode instanceof ADDDNode ) {
					maxf = ((ADDDNode)falsenode)._dUpper;
				}else {
					maxf = ((ADDINode)falsenode)._dMaxLower;
				}
				
				if( max == maxf ) {
					if( defaultval != false ) {
						actAssigns.put(var, false);
					}
					nod = falsenode;
				}else {
					if( defaultval != true ) {
						actAssigns.put(var, true);
					}
					nod = truthnode;
				}
			}
		}
		
		Map<Integer, Boolean> intAssigns = convertToInts(actAssigns);
		
		return _context.getConstantNode(getJointActionID(intAssigns));
	}
	
	/**
	 * @param pActAssigns
	 * @return
	 */
	


	/* (non-Javadoc)
	 * @see mdp.id.RDDL2DD#thresholdAt(int, double, int[])
	 */
	@Override
	public int thresholdAt(int dd, double val, boolean exact) {
		int ret = _context.AddtoBdd(dd, val, exact);
//	  if( _debug.compareTo(DEBUG.SOLUTION_INFO) >= 0 ){
//      		checkConstraints( ret, dd );
//      }
//		System.out.println(_context.threshHit);
		_context.threshHit = 0;
		_context.threshCache.clear();
		return ret;
	}


	/* (non-Javadoc)
	 * @see mdp.id.RDDL2DD#getActualVarName(int)
	 */
	@Override
	public String getActualVarName(int ddID) {
		return _tmID2VarName.get(_context._hmID2VarName.get(ddID));
	}


	/* (non-Javadoc)
	 * @see mdp.id.RDDL2DD#approximate(int, double)
	 */
	@Override
	public int approximate(int dd, double epsilon) {
		_context.PRUNE_PRECISION = epsilon;
		_context.PRUNE_TYPE = DD.REPLACE_AVG;
		int ret = _context.pruneNodes(dd);
	  if( _debug.compareTo(DEBUG.SOLUTION_INFO) >= 0 ){
  		checkConstraints( ret );
      }
	  return ret;
	}

	/* (non-Javadoc)
	 * @see mdp.id.RDDL2DD#swapForPrimes(int)
	 */
	@Override
	public int swapForPrimes(int dd) {
		int ret = _context.remapGIDsInt(dd, _hmPrimeRemap);
		if( !_context.verifyOrder(ret) ) {
			ret = _context.align(ret);
		}
		return ret;
	}

	/* (non-Javadoc)
	 * @see mdp.id.RDDL2DD#toContextID(java.lang.String)
	 */
	@Override
	public int toContextID(String varname) {
		return (Integer)_context._hmVarName2ID.get((_tmVarName2ID.get(varname)));
	}

	/* (non-Javadoc)
	 * @see mdp.id.RDDL2DD#makePrimeRemap()
	 */
	@Override
	protected void makePrimeRemap() {
		for( String s : _alstatevars ) {
			String ns = s + "'";
			_hmPrimeRemap.put(toContextID(s), 
					toContextID(ns));
		}
	}

	@Override
	protected String toContextString(String varname) {
		return String.valueOf(_context._hmID2VarName.get(_tmVarName2ID.get(varname)));
	}

	/* (non-Javadoc)
	 * @see mdp.id.RDDL2DD#makeNegInfDD(int)
	 */
	@Override
	protected int makeNegInfDD(int dd, double val){
		int ret = makeNegInfDDHelper(dd, val);
//		System.out.println(axHit);
		makeNegInfCache.clear();
		return ret;
	}
	
	private int makeNegInfDDHelper(int dd, double val ) {
		
		if( makeNegInfCache .get(new Pair<Integer, Double>(dd, val) ) != null ) {
//			++argMaxHit;
			return makeNegInfCache.get(new Pair<Integer, Double>(dd, val) );
		}
		
		ADDNode node = _context.getNode(dd);
		if( node instanceof ADDINode ) {
			ADDINode inode = (ADDINode)node;
			int truedd = makeNegInfDD(inode._nHigh, val);
			int falsedd = makeNegInfDD(inode._nLow, val);
			
			int ret = combine(inode._nTestVarID, truedd, falsedd);
			
			makeNegInfCache.put(new Pair<Integer, Double>(dd, val), ret);
			return ret;
		}else {
			ADDDNode dnoe = (ADDDNode)node;
			if( dnoe._dUpper == val ) {
				return DD_NEG_INF;
			}else {
				return DD_ZERO;
			}
		}
	}

//	/* (non-Javadoc)
//	 * @see mdp.id.RDDL2DD#fixInPlace(int)
//	 */
//	@Override
//	public int fix(int dd) {
//		int ret = fixHelper(dd);
//		return ret;
//	}
//
//	/**
//	 * @param pNode
//	 */
//	private int fixHelper(int dd) {
//		
//		ADDNode node = _context.getNode(dd);
//		
//		if( node instanceof ADDDNode ) {
//			double leafval = ((ADDDNode)node)._dUpper;
//			if( leafval == getNegInf() ){
//				return DD_ZERO;
//			}else{
//				return dd;
//			}
//		}
//		
//		ADDINode inode = (ADDINode)node;
//		if( inode._dMinLower != getNegInf() ) {
//			return dd;
//		}
//		
//		int truebr;
//		int truedd = fixHelper(inode._nHigh);
//		truebr = _context.getVarNode(inode._nTestVarID, 0.0d, 1.0d);
//		truebr = _context.applyInt(truebr, truedd, DD.ARITH_PROD);
//
//
//		int falsebr;
//		int falsedd = fixHelper(inode._nLow);
//		falsebr = _context.getVarNode(inode._nTestVarID, 1.0d, 0.0d);
//		falsebr = _context.applyInt(falsebr, falsedd, DD.ARITH_PROD);
//		
//		return _context.applyInt(falsebr, truebr, DD.ARITH_SUM);
//		
//	}

	/* (non-Javadoc)
	 * @see mdp.id.RDDL2DD#getNewContext()
	 */
	@Override
	public Pair<ADD, TreeMap<Integer, String>> getNewContext() {
		return new Pair<ADD, TreeMap<Integer, String>>(new ADD(_alOrder), toContextIDs(_tmVarName2ID));
	}

	@Override
	public String getVarName(int id) {
		return _tmID2VarName.get(id);
	}

	/* (non-Javadoc)
	 * @see mdp.id.RDDL2DD#addPolicyConstraint(int)
	 */
	@Override
	public void addPolicyConstraint(int pPi) {
	  if( _debug.compareTo(DEBUG.SOLUTION_INFO) >= 0 ){
  		checkConstraints( pPi );
      }
		  
		_hsPairConstraints.add(new Pair<Integer, Integer>(pPi, -1));
	}

	/* (non-Javadoc)
	 * @see mdp.id.RDDL2DD#removePolicyConstraint(int)
	 */
	@Override
	public void removePolicyConstraint(int pPi) {
		boolean ret = _hsPairConstraints.remove(new Pair<Integer, Integer>(pPi, -1));
		if( !ret ){
			System.err.println("could not find to remove the policy constraint");
			System.exit(1);
		}
	}

	@Override
	public int makeBDDDetPolicy(int bdd){
		
		int ret = makeBDDDetPolicyInt(bdd);
		detPolicyCache.clear();
		
//	  if( _debug.compareTo(DEBUG.SOLUTION_INFO) >= 0 ){
//  		checkConstraints( bdd, ret );
//      }
		  
		return ret;
		
	}

	private int makeBDDDetPolicyInt(int bdd){
		
		int ret = 0;
		
		if( detPolicyCache.get(bdd) == null ){
			ADDNode node = _context.getNode(bdd);
			if( node instanceof ADDDNode ){
				return bdd;
			}else{
				ADDINode inode = (ADDINode)node;
				String var = getActualVarName(inode._nTestVarID);
				
				int truedd = makeBDDDetPolicyInt(inode._nHigh);
				int falsedd = makeBDDDetPolicyInt(inode._nLow);
				
				int truebr = _context.getVarNode(inode._nTestVarID, 0.0d, 1.0d);
				int falsebr = _context.getVarNode(inode._nTestVarID, 1.0d, 0.0d);
				
				int falsestatedd = falsedd;
				
				if( _alactionvars.contains(var) ){
					
					int ind = _alactionvars.indexOf(var);
					for( int i = ind; i < _alactionvars.size(); ++i ){
						String other = _alactionvars.get(i);
						falsestatedd = _context.opOut(falsestatedd, toContextID(other), DD.LOG_OR);
						falsestatedd = ConstrainThis(falsestatedd, null, false);
						falsestatedd = fix(falsestatedd);
						
						if( _debug.compareTo(DEBUG.SOLUTION_INFO) >= 0 ){
							
							checkConstraints(falsestatedd);
							
							if( _context.getGIDs(falsestatedd).contains(toContextID(other)) ){
								System.err.println("Boink!");
							}
							
						}
					}
					
//					truestatedd = ConstrainThis(truestatedd, null, false);
					
					Set gids2 = _context.getGIDs(falsestatedd);
					
					for( String a : _alactionvars ){
						int id = toContextID(a);
						if( gids2.contains(id) ){
							System.err.println("action variable in state dd");
							System.exit(1);
						}
					}
					
					truedd = _context.applyInt(truedd,
								_context.applyInt(DD_ONE, falsestatedd, DD.ARITH_MINUS), 
								DD.ARITH_PROD);
					
					ret = _context.applyInt(
							_context.applyInt(truedd, truebr, DD.ARITH_PROD), 
							_context.applyInt(falsedd, falsebr, DD.ARITH_PROD), 
							DD.ARITH_SUM);
					
				}else{
					
					ret = _context.applyInt(
							_context.applyInt(truedd, truebr, DD.ARITH_PROD),
							_context.applyInt(falsedd, falsebr, DD.ARITH_PROD),
							DD.ARITH_SUM);
					
				}

				ret = ConstrainThis(ret, null, false);
				ret = fix(ret);
				
//				if( _context.applyInt(bdd, ret, DD.ARITH_PROD) != ret ){
//					System.err.println("Incorrect det policy");
//					System.exit(1);
//				}
				
				detPolicyCache.put(bdd, ret);
			}
		}
		
		return detPolicyCache.get(bdd);
	}

	@Override
	public int computeExpectation(int dd, int id, Map<Integer, Boolean> act,
			boolean flush, int... save)  {
	
		if( _debug.compareTo(DEBUG.SOLUTION_INFO) >= 0 ) {
			System.out.println("Expectation " + getVarName(id) + " " + getSize(dd) );
			System.out.println(showMemory());
		}
		
		expectationCache.clear();

		int ret = computeExpectationInt(dd, id, act);
		
		ret = ConstrainThis(ret, act, true, save);
		
		if( flush ) {
			saveThis(ret);
			flushCaches(false, save);
			forgetThis(ret);	
		}
		
		return ret;
	}

	@Override
	public ArrayList<Long> countLeaves(final int... dds){
		ArrayList<Long> ret = new ArrayList<Long>();
		HashSet set = new HashSet();
		for( int i : dds ){
			_context.collectLeaves(i, set);
			ret.add((long) set.size());
			set.clear();
		}
		return ret;
	}
	
	private int computeExpectationInt(int dd, int id, Map<Integer, Boolean> act) {
		
		if( expectationCache.get(new Pair<Integer, Integer>(dd, id) ) == null ){
			
			int ret = 0;
			
			ADDNode node = _context.getNode(dd);
			
			if( node instanceof ADDINode &&
					((ADDINode)node)._nTestVarID == toContextID(getVarName(id)) ){
				int dual = getDualDiagram(id, act);
				
//				System.out.println("simplify " + assign);
				
//				dual = simplify(dual, assign);
				
				ret = _context.applyInt(dd, dual, DD.ARITH_PROD);
				ret = _context.opOut(ret, toContextID(_tmID2VarName.get(id)), DD.ARITH_SUM);
			}else if( node instanceof ADDDNode
					|| _context.comesBefore(toContextID(getVarName(id)),
							((ADDINode)node)._nTestVarID) ){
//				System.out.println("end");
				ret = dd;
			}else{
				if( !_context.comesBefore(((ADDINode)node)._nTestVarID,
						toContextID(getVarName(id))) ){
					System.err.println("err");
					System.exit(1);
				}
				//recurse
				ADDINode inode = (ADDINode)node;
//				System.out.println("set " + varid + " "  + name + " " + true);
				int truebr = computeExpectationInt(inode._nHigh, id, act);
				
				int falsebr = computeExpectationInt(inode._nLow, id, act);
				
				ret = combine(inode._nTestVarID, truebr, falsebr);
				
			}
			expectationCache.put(new Pair<Integer, Integer>(dd, id), ret);
		}
		
		int ret = expectationCache.get(new Pair<Integer, Integer>(dd, id));
		
		return ret;
		
	}	
	
}